//===-- DWARFDebugMacro.cpp -------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "DWARFDebugMacro.h"
#include "SymbolFileDWARF.h"

#include "lldb/Symbol/DebugMacros.h"

#include "DWARFDataExtractor.h"

using namespace lldb_private;

DWARFDebugMacroHeader
DWARFDebugMacroHeader::ParseHeader(const DWARFDataExtractor &debug_macro_data, lldb::offset_t *offset)
{
    DWARFDebugMacroHeader header;

    // Skip over the version field in header.
    header.m_version = debug_macro_data.GetU16(offset);

    uint8_t flags = debug_macro_data.GetU8(offset);
    header.m_offset_is_64_bit = flags & OFFSET_SIZE_MASK ? true : false;

    if (flags & DEBUG_LINE_OFFSET_MASK)
    {
        if (header.m_offset_is_64_bit)
            header.m_debug_line_offset = debug_macro_data.GetU64(offset);
        else
            header.m_debug_line_offset = debug_macro_data.GetU32(offset);
    }

    // Skip over the operands table if it is present.
    if (flags & OPCODE_OPERANDS_TABLE_MASK)
        SkipOperandTable(debug_macro_data, offset);

    return header;
}

void
DWARFDebugMacroHeader::SkipOperandTable(const DWARFDataExtractor &debug_macro_data, lldb::offset_t *offset)
{
    uint8_t entry_count = debug_macro_data.GetU8(offset);
    for (uint8_t i = 0; i < entry_count; i++)
    {
        // Skip over the opcode number.
        debug_macro_data.GetU8(offset);

        uint64_t operand_count = debug_macro_data.GetULEB128(offset);

        for (uint64_t j = 0; j < operand_count; j++)
        {
            // Skip over the operand form
            debug_macro_data.GetU8(offset);
        }
    }
}

void
DWARFDebugMacroEntry::ReadMacroEntries(const DWARFDataExtractor &debug_macro_data,
                                       const DWARFDataExtractor &debug_str_data,
                                       const bool offset_is_64_bit,
                                       lldb::offset_t *offset,
                                       SymbolFileDWARF *sym_file_dwarf,
                                       DebugMacrosSP &debug_macros_sp)
{
#if TODO_REQUIRES_LLVM_ORG_SYNC
    // This code can go back in once llvm.org macros support is in the relevant GitHub llvm branch.
    llvm::dwarf::MacroEntryType type = static_cast<llvm::dwarf::MacroEntryType>(debug_macro_data.GetU8(offset));
    while (type != 0)
    {
        lldb::offset_t new_offset = 0, str_offset = 0;
        uint32_t line = 0;
        const char *macro_str = nullptr;
        uint32_t debug_line_file_idx = 0;

        switch (type)
        {
            case DW_MACRO_define:
            case DW_MACRO_undef:
                line = debug_macro_data.GetULEB128(offset);
                macro_str = debug_macro_data.GetCStr(offset);
                if (type == DW_MACRO_define)
                    debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateDefineEntry(line, macro_str));
                else
                    debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateUndefEntry(line, macro_str));
                break;
            case DW_MACRO_define_indirect:
            case DW_MACRO_undef_indirect:
                line = debug_macro_data.GetULEB128(offset);
                if (offset_is_64_bit)
                    str_offset = debug_macro_data.GetU64(offset);
                else
                    str_offset = debug_macro_data.GetU32(offset);
                macro_str = debug_str_data.GetCStr(&str_offset);
                if (type == DW_MACRO_define_indirect)
                    debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateDefineEntry(line, macro_str));
                else
                    debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateUndefEntry(line, macro_str));
                break;
            case DW_MACRO_start_file:
                line = debug_macro_data.GetULEB128(offset);
                debug_line_file_idx = debug_macro_data.GetULEB128(offset);
                debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateStartFileEntry(line, debug_line_file_idx));
                break;
            case DW_MACRO_end_file:
                // This operation has no operands.
                debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateEndFileEntry());
                break;
            case DW_MACRO_transparent_include:
                if (offset_is_64_bit)
                    new_offset = debug_macro_data.GetU64(offset);
                else
                    new_offset = debug_macro_data.GetU32(offset);
                debug_macros_sp->AddMacroEntry(
                    DebugMacroEntry::CreateIndirectEntry(sym_file_dwarf->ParseDebugMacros(&new_offset)));
                break;
            default:
                // TODO: Add support for other standard operations.
                // TODO: Provide mechanism to hook handling of non-standard/extension operands.
                return;
        }
        type = static_cast<llvm::dwarf::MacroEntryType>(debug_macro_data.GetU8(offset));
    }
#endif
}
